﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Entity;
using System.Text;
using iTelluro.Explorer.Domain.Seedwork;
using iTelluro.Explorer.Infrastructure.CrossCutting.Logging;
using iTelluro.Explorer.Infrastructure.Data.Seedwork.Resources;

namespace iTelluro.Explorer.Infrastructure.Data.Seedwork
{
    public class Repository<TEntity>
        : IRepository<TEntity>
        where TEntity : Entity
    {
        #region Members

        IQueryableUnitOfWork _UnitOfWork;

        #endregion
        #region Constructor

        /// <summary>
        /// Create a new instance of repository
        /// </summary>
        /// <param name="unitOfWork">Associated Unit Of Work</param>
        public Repository(IQueryableUnitOfWork unitOfWork)
        {
            if (unitOfWork == (IUnitOfWork)null)
                throw new ArgumentNullException("unitOfWork");

            _UnitOfWork = unitOfWork;
        }

        #endregion

        #region IRepository<TEntity> 成员

        public IUnitOfWork UniteOfWork
        {
            get { return _UnitOfWork; }
        }

        public virtual void Add(TEntity item)
        {
            if (item != (TEntity)null)
                GetSet().Add(item);
            else
            {
                LoggerFactory.CreateLog()
                    .LogInfo(Messages.info_CannotAddNullEntity, typeof(TEntity).ToString());
            }
        }

        public virtual void Remove(TEntity item)
        {
            if (item != (TEntity)null)
            {
                //attach item if not exist
                _UnitOfWork.Attach(item);

                //set as "removed"
                GetSet().Remove(item);
            }
            else
            {
                LoggerFactory.CreateLog()
                    .LogInfo(Messages.info_CannotRemoveNullEntity, typeof(TEntity).ToString());
            }
        }

        public virtual void Modify(TEntity item)
        {
            if (item != (TEntity)null)
            {
                _UnitOfWork.SetModified(item);
            }
            else
            {
                LoggerFactory.CreateLog()
                    .LogInfo(Messages.info_CannotModifyNullEntity, typeof(TEntity));
            }
        }

        public virtual void TrackItem(TEntity item)
        {
            if (item != (TEntity)null)
            {
                _UnitOfWork.Attach<TEntity>(item);
            }
            else
            {
                LoggerFactory.CreateLog()
                    .LogInfo(Messages.info_CannotTrackNullEntity, typeof(TEntity), ToString());
            }
        }

        public virtual void Merge(TEntity persisted, TEntity currrent)
        {
            _UnitOfWork.ApplyCurrentValues(persisted, currrent);
        }

        public virtual TEntity Get(string id)
        {
            if (!string.IsNullOrEmpty(id))
            {
                return GetSet().Find(id);
            }
            else
                return null;
        }

        public virtual TEntity Get(decimal id)
        {
            return GetSet().Find(id);

        }

        public virtual TEntity Get(int id)
        {
            return GetSet().Find(id);

        }

        public virtual IEnumerable<TEntity> GetAll()
        {
            return GetSet();
        }

        public virtual IEnumerable<TEntity> AllMatching(Domain.Seedwork.Specification.ISpecification<TEntity> specification)
        {
            return GetSet().Where(specification.SatisfiedBy());
        }

        public virtual IEnumerable<TEntity> GetPaged<KProperty>(int pageIndex, int pageCount, System.Linq.Expressions.Expression<Func<TEntity, KProperty>> orderByExpression, bool ascending)
        {
            var set = GetSet();

            if (ascending)
            {
                return set.OrderBy(orderByExpression)
                    .Skip(pageCount * pageIndex)
                    .Take(pageCount);
            }
            else
            {
                return set.OrderByDescending(orderByExpression)
                    .Skip(pageCount * pageIndex)
                    .Take(pageCount);
            }
        }

        public virtual IEnumerable<TEntity> GetFiltered(System.Linq.Expressions.Expression<Func<TEntity, bool>> filter)
        {
            return GetSet().Where(filter);
        }

        #endregion

        #region IDisposable 成员

        public void Dispose()
        {
            if (_UnitOfWork != null)
                _UnitOfWork.Dispose();
        }

        #endregion

        #region private method

        IDbSet<TEntity> GetSet()
        {
            return _UnitOfWork.CreateSet<TEntity>();
        }

        #endregion

        #region IRepository<TEntity> 成员


        public virtual IEnumerable<TEntity> GetPaged<KProperty>(int pageIndex, int pageCount, Domain.Seedwork.Specification.ISpecification<TEntity> filterSpecification, System.Linq.Expressions.Expression<Func<TEntity, KProperty>> orderByExpression, bool ascending)
        {
            var set = GetSet().Where(filterSpecification.SatisfiedBy());

            if (ascending)
            {
                return set.OrderBy(orderByExpression)
                    .Skip(pageCount * pageIndex)
                    .Take(pageCount);
            }
            else
            {
                return set.OrderByDescending(orderByExpression)
                    .Skip(pageCount * pageIndex)
                    .Take(pageCount);
            }
        }

        public virtual int GetRecordCount(Domain.Seedwork.Specification.ISpecification<TEntity> filterSpecification)
        {
            var set = GetSet().Where(filterSpecification.SatisfiedBy());
            return set.Count();
        }
        #endregion
    }
}
